package com.jianli.smart.aop;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jianli.smart.aop.annotation.Aspect;
import com.jianli.smart.aop.demo.TranscationProxy;
import com.jianli.smart.core.BeanHelper;
import com.jianli.smart.core.ClassHelper;
import com.jianli.smart.core.annotation.Service;

public final class AopHelper {
	private final static Logger LOGGER = LoggerFactory.getLogger(AopHelper.class); 
	
	// 返回执行的类与切面列表的映射关系：比如一个userContrller 可能同时被日志aspect和计时aspect所代理
	private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
		Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>,List<Proxy>>();
		for(Map.Entry<Class<?>,Set<Class<?>>> entry:proxyMap.entrySet()){
			Class<?> proxyClass = entry.getKey();
			Set<Class<?>> targetClassSet = entry.getValue();
			for(Class<?> targetClass : targetClassSet){
				Proxy proxy = (Proxy)proxyClass.newInstance();
				if(targetMap.containsKey(targetClass)){
					targetMap.get(targetClass).add(proxy);
				}else{
					List<Proxy> proxyList = new ArrayList<Proxy>();
					proxyList.add(proxy);
					targetMap.put(targetClass, proxyList);
				}
			}
		}
		return targetMap;
		
	}
	
	// 返回AOP切面与AOP作用类的映射关系
	private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception{
		Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>,Set<Class<?>>>();
		addAspectProxy(proxyMap);
		addTranscationProxy(proxyMap);
		return proxyMap;
		
	}
	
	// 找到所有Aspect注解作用的类
	private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception{
		Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
		Class<? extends Annotation> annotation = aspect.value();
		if(annotation != null && !annotation.equals(Aspect.class)){
			targetClassSet.addAll(ClassHelper.getSet(annotation));
		}
		return targetClassSet;
	}
	
	private static void addTranscationProxy(Map<Class<?>, Set<Class<?>>> proxyMap){
		Set<Class<?>> serviceClassSet = ClassHelper.getSet(Service.class);
		proxyMap.put(TranscationProxy.class, serviceClassSet);
	}
	
	private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception{
		Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
		
		for(Class<?> proxyClass:proxyClassSet){
			if(proxyClass.isAnnotationPresent(Aspect.class)){
				Aspect aspect = proxyClass.getAnnotation(Aspect.class);
				Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
				proxyMap.put(proxyClass, targetClassSet);
			}
		}
	}
	
	static {
		try{
			Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
			Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
			for(Map.Entry<Class<?>, List<Proxy>> entry:targetMap.entrySet()){
				Class<?> targetClass = entry.getKey();
				List<Proxy> targetProxyList = entry.getValue();
				Object proxy = ProxyManager.createProxy(targetClass, targetProxyList);
				BeanHelper.setBean(targetClass, proxy);
				System.out.println(targetClass.getName()+":"+proxy.getClass().getName());
			}
		}catch (Exception e){
			LOGGER.error("aop failue:",e);;
		}
	}
}
